using System;
using System.IO;
using ParserGenerator.Extensions;

namespace ParserGenerator.Input
{
    [Serializable]
    public struct SourceLocation
    {
        public SourceDocument Document { get; }
        public int Index { get; }
        public int Line { get; }
        public int Offset { get; }
        public int Column { get; }

        public SourceLocation(SourceDocument document, int index, int line, int offset, int column)
        {
            if (document == null)
                throw new ArgumentNullException(nameof(document));
            if (index < 0)
                throw new ArgumentOutOfRangeException(nameof(index), SR.IndexMustBeNonNegative);
            if (line < 0)
                throw new ArgumentOutOfRangeException(nameof(line), SR.LineMustBeNonNegative);
            if (offset < 0)
                throw new ArgumentOutOfRangeException(nameof(offset), SR.OffsetMustBeNonNegative);
            if (column < 0)
                throw new ArgumentOutOfRangeException(nameof(column), SR.ColumnMustBeNonNegative);
            Document = document;
            Index = index;
            Line = line;
            Offset = offset;
            Column = column;
        }

        public override string ToString()
        {
            return string.Format("{0}({1},{2})", Document.Name, Line + 1, Offset + 1);
        }

        public void Print()
        {
            Print(Console.Error);
        }

        public void Print(TextWriter writer)
        {
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));

            // Print file & position
            using (ConsoleExtensions.PromptColor.AsForegroundColor())
            {
                writer.Write("  File \"");
                using (ConsoleExtensions.HighlightColor.AsForegroundColor())
                    writer.Write(Document.Name);
                writer.Write("\", line ");
                using (ConsoleExtensions.HighlightColor.AsForegroundColor())
                    writer.Write(Line + 1);
                writer.Write(", offset ");
                using (ConsoleExtensions.HighlightColor.AsForegroundColor())
                    writer.Write(Offset + 1);
                writer.Write(", column ");
                using (ConsoleExtensions.HighlightColor.AsForegroundColor())
                    writer.Write(Column + 1);
                writer.WriteLine();
            }

            // Find text range
            string text = Document.TryGetLineAtCharIndex(Index);
            if (text == null)
                return;
            int i = 0;
            while (i < text.Length && text[i].IsWhiteSpace())
                i++;
            int j = text.Length;
            while (j > i && text[j - 1].IsWhiteSpace())
                j--;
            const int total = 60;
            int left = Offset - total / 2;
            if (left < i)
                left = i;
            int right = left + total;
            if (right > j)
                right = j;

            // Print surrounding text
            using (ConsoleExtensions.PromptColor.AsForegroundColor())
            {
                writer.Write("    ");
                if (i < left)
                    using (ConsoleExtensions.RemovedColor.AsForegroundColor())
                        writer.Write("... ");
                string str = text.Substring(left, right - left);
                writer.Write(str);
                if (right < j)
                    using (ConsoleExtensions.RemovedColor.AsForegroundColor())
                        writer.Write(" ...");
                writer.WriteLine();
            }

            // Print anchor
            using (Console.BackgroundColor.AsForegroundColor())
            {
                writer.Write("    ");
                if (i < left)
                    writer.Write("    ");
                int length = Math.Max(Offset - left, 0);
                string str = text.Substring(left, length);
                writer.Write(str);
                using (ConsoleExtensions.HighlightColor.AsForegroundColor())
                    writer.Write("^");
                writer.WriteLine();
            }
        }
    }
}
